package org.example.idempotent;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.expression.MethodBasedEvaluationContext;
import org.springframework.core.StandardReflectionParameterNameDiscoverer;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class ExpressionUtils {

    private static final Map<String, Expression> EXPRESSION_CACHE = new ConcurrentHashMap<>(64);

    private static final ExpressionParser SPEL_PARSER = new SpelExpressionParser();

    /**
     * 可以通过别名获取表达式的值,类似于spring cache的用法 可以给参数指定别名
     *
     * @param arguments         方法
     * @param method            参数
     * @param expressionsString Spring EL表达式字符串
     * @param <T>               类型
     * @return 结果集
     */
    @Nullable
    public static <T> T[] getExpressionValueAliasAble(@Nullable Object[] arguments, @NonNull Method method, String... expressionsString) {
        if (ArrayUtils.isEmpty(arguments) || ArrayUtils.isEmpty(expressionsString)) {
            return null;
        }

        Object[] result = new Object[expressionsString.length];
        for (int i = 0; i < result.length; i++) {
            result[i] = getExpressionValueAliasAble(arguments, method, expressionsString[i]);
        }

        //noinspection unchecked
        return (T[]) result;
    }

    /**
     * 可以通过别名获取表达式的值,类似于spring cache的用法 可以给参数指定别名
     *
     * @param arguments        参数
     * @param method           方法
     * @param expressionString Spring EL表达式字符串
     * @param <T>              类型
     * @return 结果
     */
    @Nullable
    public static <T> T getExpressionValueAliasAble(@Nullable Object[] arguments, @NonNull Method method, String expressionString) {
        if (ArrayUtils.isEmpty(arguments) || StringUtils.isBlank(expressionString)) {
            return null;
        }
        Expression expression = getExpression(expressionString);

        if (expression == null) {
            return null;
        }

        MethodBasedEvaluationContext evaluationContext = getEvaluationContextAliasAble(arguments, method);
        return (T) expression.getValue(evaluationContext);
    }

    /**
     * 获取Expression对象
     *
     * @param expressionString Spring EL 表达式字符串 例如 #{param.id}
     * @return Expression
     */
    @Nullable
    public static Expression getExpression(@Nullable String expressionString) {
        if (StringUtils.isBlank(expressionString)) {
            return null;
        }

        if (EXPRESSION_CACHE.containsKey(expressionString)) {
            return EXPRESSION_CACHE.get(expressionString);
        }

        Expression expression = SPEL_PARSER.parseExpression(expressionString);
        EXPRESSION_CACHE.put(expressionString, expression);
        return expression;
    }

    /**
     * 获取可以通过别名查找的EvaluationContext,类似于spring cache的用法 #a0.id,#p1.name
     *
     * @param arguments 方法入参
     * @param method    方法
     * @return MethodBasedEvaluationContext
     */
    @NonNull
    public static MethodBasedEvaluationContext getEvaluationContextAliasAble(@NonNull Object[] arguments, @NonNull Method method) {
        return new MethodBasedEvaluationContext(arguments, method, arguments, new StandardReflectionParameterNameDiscoverer());
    }



}
